package com.kris;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.WorkbookUtil;

/**
 * 当月倒休统计报表 <br>
 * 格式为： <br>
 * <pre>
 * 姓名 11-1, 11-2, 11-3, ... 11-30 合计
 * 张三  2      0     0           1     3
 * </pre>
 *
 * @author zhangqunshi
 */
class TimeCompensationReport extends Report {

    final long ONE_DAY = 24 * 60 * 60 * 1000;
    final String title = "倒休统计报表";
    private final List<String> employeeList; // 员工列表
    private final Map<String, EmployeeAttendRecord> records; //考勤记录
    private final String filename; //输出文件
    private final List<Date> holidays;  //所有节假日
    private final List<Date> excludeHolidays; // 排除节假日
    private final String companyBeginTime;  // 公司上班时间
    private final String companyEndTime; // 公司下班时间
    private final double lateWorkTime;
    private final int lateHour;
    private final int lateMin;

    public TimeCompensationReport(String excelFileName,
            List<String> employeeList,
            Map<String, EmployeeAttendRecord> records,
            List<Date> holidays,
            List<Date> excludeHolidays,
            String companyBeginTime,
            String companyEndTime,
            int lateWorkTime) {
        this.filename = excelFileName;
        this.employeeList = employeeList;
        this.records = records;
        this.holidays = holidays;
        this.excludeHolidays = excludeHolidays;
        this.companyBeginTime = companyBeginTime;
        this.companyEndTime = companyEndTime;
        this.lateWorkTime = lateWorkTime;
        // TODO 没时间弄了，直接用实例变量
        this.lateHour = lateWorkTime / 60; //迟到小时
        this.lateMin = lateWorkTime % 60; //迟到分钟
    }

    @Override
    public void make() throws FileNotFoundException, IOException, ParseException, Exception {
        Date firstDay = getFirstDay(this.records);
        Date lastDay = getLastDay(this.records);
        if (firstDay == null || lastDay == null) {
            throw new Exception("无法建立倒休统计表，因为没有找到开始时间(" + firstDay + ")或结束时间(" + lastDay + ")");
        }

        // 新建一个excel
        Workbook wb = new HSSFWorkbook();

        //工作簿
        String safeName = WorkbookUtil.createSafeSheetName("sheet1");
        Sheet sheet = wb.createSheet(safeName);

        // 建立第一个行， 把表的标题放到这里
        Row firstRow = sheet.createRow((short) 0);
        CellStyle titleStyle = wb.createCellStyle();
        titleStyle.setAlignment(HorizontalAlignment.CENTER); // 设置字体居中显示
        titleStyle.setFont(createFontArial18(wb));
        Cell titleCell = firstRow.createCell(0);
        titleCell.setCellValue(title);
        titleCell.setCellStyle(titleStyle);

        // 合并第一行表格
        sheet.addMergedRegion(new CellRangeAddress(
                0, //first row (0-based)
                0, //last row  (0-based)
                0, //first column (0-based)
                3 //last column  (0-based)
        ));

        // 建立表头样式, 设置背景颜色
        CellStyle headerStyle = wb.createCellStyle();
        headerStyle.setAlignment(HorizontalAlignment.CENTER); // 设置字体居中显示
        headerStyle.setFont(createFontArial12(wb));

        CellStyle yellowHeaderStyle = wb.createCellStyle();
        yellowHeaderStyle.setFillForegroundColor(IndexedColors.YELLOW.index);
        yellowHeaderStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        yellowHeaderStyle.setAlignment(HorizontalAlignment.CENTER); // 设置字体居中显示
        yellowHeaderStyle.setFont(createFontArial12(wb));
        yellowHeaderStyle.setBorderBottom(BorderStyle.THIN);
        yellowHeaderStyle.setBorderLeft(BorderStyle.THIN);
        yellowHeaderStyle.setBorderTop(BorderStyle.THIN);
        yellowHeaderStyle.setBorderRight(BorderStyle.THIN);
        yellowHeaderStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());

        // 建立第2行, 设置表头, 表头是日期, 需要循环显示每一天
        Row secondRow = sheet.createRow((short) 1);
        Cell nameCell = secondRow.createCell(0);
        nameCell.setCellValue("姓名");
        nameCell.setCellStyle(headerStyle);

        long firstDayTime = firstDay.getTime();
        long lastDayTime = lastDay.getTime();

        int colIndex = 1;
        // 记录日期与列索引的对应关系
        Map<Integer, Date> columnIndexMap = new HashMap<Integer, Date>();
        for (long j = firstDayTime; j <= lastDayTime; j += ONE_DAY) {
            Date currDate = new Date(j);
            String currDay = DateUtil.format(currDate, "MM-dd");
            Cell headerCell = secondRow.createCell(colIndex);
            headerCell.setCellValue(currDay);
            if (DateUtil.isWeekendOrHoliday(currDate,
                    this.holidays,
                    this.excludeHolidays)) {
                headerCell.setCellStyle(yellowHeaderStyle);
            } else {
                headerCell.setCellStyle(headerStyle);
            }

            columnIndexMap.put(colIndex, currDate);
            colIndex++;
        }
        // 合计表头
        Cell totalCell = secondRow.createCell(colIndex + 1);
        totalCell.setCellValue("合计");
        totalCell.setCellStyle(headerStyle);

        // ---------------------------------------------
        //                表格数据
        // ---------------------------------------------
        // 表格数据格式
        CellStyle dataStyle = wb.createCellStyle();
        dataStyle.setBorderBottom(BorderStyle.THIN);
        dataStyle.setBorderLeft(BorderStyle.THIN);
        dataStyle.setBorderTop(BorderStyle.THIN);
        dataStyle.setBorderRight(BorderStyle.THIN);
        dataStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());

        // 黄色背景, 表示节假日
        CellStyle yellowStyle = getYellowStyle(wb);

        int rowIndex = 2; //员工数据开始的行数
        // 获取所有员工名单
        // 循环处理每个员工的时间
        for (int i = 0; i < employeeList.size(); i++) {
            String employeeName = employeeList.get(i);

            // 获取此员工所有的加班信息记录
            EmployeeAttendRecord em = records.get(employeeName);
            if (em == null) {
                // 忽略不存在考勤记录的员工
                continue;
            }

            createRow(sheet, rowIndex,
                    employeeName,
                    em.getWorkTimeRecords(),
                    columnIndexMap,
                    dataStyle,
                    yellowStyle);
            rowIndex++;

        } // end for

        FileOutputStream fileOut = new FileOutputStream(this.filename);
        wb.write(fileOut);
        fileOut.close();
    }

    Date getFirstDay(Map<String, EmployeeAttendRecord> records) throws ParseException {
        // 计算统计表的第一天和最后一天
        Date d = null;

        for (EmployeeAttendRecord record : records.values()) {
            Date first = record.getFirstDay();
            if (d == null) {
                d = first;
            }
            if (first != null && d != null && first.before(d)) {
                d = first;
            }
        }
        return d;
    }

    Date getLastDay(Map<String, EmployeeAttendRecord> records)
            throws ParseException {
        // 计算统计表的第一天和最后一天
        Date d = null;

        for (EmployeeAttendRecord record : records.values()) {
            Date last = record.getLastDay();
            if (d == null) {
                d = last;
            }
            if (last != null && d != null && last.after(d)) {
                d = last;
            }
        }
        return d;
    }

    /**
     * 建立一行数据, 一行一个员工
     *
     * @param sheet
     * @param rowIndex
     * @param employeeName
     * @param worktimes
     * @param columnIndexMap
     * @throws ParseException
     * @throws Exception
     */
    private void createRow(Sheet sheet, int rowIndex,
            String employeeName, List<EmployeeWorkTime> worktimes,
            Map<Integer, Date> columnIndexMap,
            CellStyle dataStyle,
            CellStyle yellowStyle) throws ParseException, Exception {
        Row row = sheet.createRow(rowIndex);
        // 员工姓名
        Cell cell0 = row.createCell(0);
        cell0.setCellValue(employeeName);
        cell0.setCellStyle(dataStyle);

        // 显示每天的考勤情况
        double totalOvertime = 0;
        // 每天都发生了什么事情的Map，例如：加班时长，未打卡
        Map<Date, String> eventMap = new HashMap<Date, String>();
        for (int k = 0; k < worktimes.size(); k++) {
            EmployeeWorkTime employeeWorkTime = worktimes.get(k);

            Date beginDate = employeeWorkTime.getBeginTime();
            Date endDate = employeeWorkTime.getEndTime();

            if (beginDate == null && endDate == null) {
                //未打卡不管
                continue;
            }

            Date workingDate = employeeWorkTime.getWorkDate();
            if (beginDate != null && endDate == null) {
                eventMap.put(workingDate, "下午异常");
                continue;
            }
            if (beginDate == null && endDate != null) {
                eventMap.put(workingDate, "上午异常");
                continue;
            }

            // 如果是周末，计算加班时长, 忘记打卡的不计算
            if (DateUtil.isWeekendOrHoliday(workingDate,
                    this.holidays,
                    this.excludeHolidays)) {
                double overtime = calculateHolidayWorkOverTime(
                        employeeWorkTime.getBeginTime(),
                        employeeWorkTime.getEndTime());
                totalOvertime += overtime;

                if (overtime > 0) {
                    eventMap.put(workingDate, "" + overtime);
                } else {
                    // 周末没来加班
                    eventMap.put(workingDate, "OK");
                }

            } else {
                // 工作日
                StringBuilder event = new StringBuilder();
                String scope = DateUtil.format(beginDate, "HH:mm") + "~" + DateUtil.format(endDate, "HH:mm");

                if (isTimeAfter(beginDate, this.companyBeginTime)) {
                    event.append("迟到");
                }

                if (isTimeBefore(endDate, this.companyEndTime)) {
                    if ("".equals(event.toString())) {
                        event.append("早退");
                    } else {
                        event.append("|早退");
                    }
                }
                if ("".equals(event.toString())) {
                    // 正常上班
                    eventMap.put(workingDate, "OK");
                } else {
                    eventMap.put(workingDate, event.toString() + scope);
                }

            }

        }//end for

        int max = 1;
        for (Integer colIndex : columnIndexMap.keySet()) {
            Cell c = row.createCell(colIndex);
            Date workingDate = columnIndexMap.get(colIndex);
            String event = eventMap.get(workingDate);
            if (DateUtil.isWeekendOrHoliday(workingDate,
                    this.holidays,
                    this.excludeHolidays)) {
                // 节假日可以不打卡，所以不是打卡异常
                if (event == null) {
                    event = "";
                }
                // TODO 可以转成数字
                c.setCellStyle(yellowStyle);
            } else if ("OK".equals(event)) {
                // 正常上班不显示
                event = "";
                c.setCellStyle(dataStyle);
            } else if (event == null) {
                event = "异常";
                c.setCellStyle(dataStyle);
            }
            c.setCellValue(event);

            max++;
        }

        // 周末加班时长合计(小时)
        Cell totalCell = row.createCell(max + 1);
        totalCell.setCellValue(totalOvertime);
        totalCell.setCellStyle(dataStyle);
    }

    /**
     * 比较参数date的小时和分钟是否早于(包括等于)公司下班时间
     *
     * @param date
     * @param time 格式：09:00 - 早上9点
     * @return
     */
    private boolean isTimeBefore(Date date, String time) throws Exception {
        // TODO 早退还需要看一下上班的时间
        try {
            String[] hhmm = time.split(":");
            int hour = Integer.parseInt(hhmm[0]);
            int min = Integer.parseInt(hhmm[1]);

            Calendar c = Calendar.getInstance();
            c.setTime(date);
            int ehour = c.get(Calendar.HOUR_OF_DAY);
            int emin = c.get(Calendar.MINUTE);

            if (ehour >= hour) {
                return false;
            }
            if (ehour == hour && emin > min) {
                return false;
            }
        } catch (Exception e) {
            throw new Exception("公司上班时间设定格式错误，应该为HH:mm。例如: 9:00");
        }

        return true;
    }

    /**
     * 比较参数date的小时和分钟是否迟于(包括等于)公司上班时间
     *
     * @param date
     * @param time
     * @return
     */
    private boolean isTimeAfter(Date date, String time) throws Exception {
        try {
            String[] hhmm = time.split(":");
            int hour = Integer.parseInt(hhmm[0]);
            int min = Integer.parseInt(hhmm[1]);
            hour += this.lateHour;
            min += this.lateMin;

            // TODO 没有计算旷工时间
            Calendar c = Calendar.getInstance();
            c.setTime(date);
            int ehour = c.get(Calendar.HOUR_OF_DAY);
            int emin = c.get(Calendar.MINUTE);

            if (ehour <= hour) {
                return false;
            }
            if (ehour == hour && emin < min) {
                return false;
            }
        } catch (Exception e) {
            throw new Exception("公司下班时间设定格式错误，应该为HH:mm。例如: 9:00");
        }

        return true;
    }

    CellStyle getYellowStyle(Workbook wb) {
        CellStyle s = wb.createCellStyle();
        s.setFillForegroundColor(IndexedColors.YELLOW.index);
        s.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        s.setBorderBottom(BorderStyle.THIN);
        s.setBorderLeft(BorderStyle.THIN);
        s.setBorderTop(BorderStyle.THIN);
        s.setBorderRight(BorderStyle.THIN);
        s.setBottomBorderColor(IndexedColors.BLACK.getIndex());
        return s;
    }

//    public static void main(String[] args) throws Exception {
//        System.out.println(TimeCompensationReport.isTimeAfter(new Date(), "19:25"));
//    }
}
